//
//  UIView_animation.swift
//  boleh
//
//  Created by BO ZHOU on 2020/8/28.
//  Copyright © 2020 BO ZHOU. All rights reserved.
//

import UIKit

private var animationStatusKey = "animationTypeKey"

public extension UIView {

    /**
     * 功能: 添加弹性动画
     * 来源: 高德demo, 点聚合弹性动画
     */
    func bounceAnimation() {
        
        let animation = CAKeyframeAnimation(keyPath: "transform.scale")
        
        animation.values = [0.05, 1.1, 0.9, 1]
        animation.duration = 0.6
        
        var timingFunctions = [CAMediaTimingFunction]()
        
        for _ in animation.values! {
            
            let functionName = CAMediaTimingFunction(name: .easeInEaseOut)
            timingFunctions.append(functionName)
        }
        
        animation.timingFunctions = timingFunctions
        
        animation.isRemovedOnCompletion = false
        self.layer.add(animation, forKey: "bounce")
    }
    
    
    /**
     CABasicAnimation *animation=[CABasicAnimation animationWithKeyPath:@"transform.scale"];
     animation.fromValue=[NSNumber numberWithFloat:0.5];
     animation.toValue=[NSNumber numberWithFloat:1.0];
     animation.duration=1.0;
     animation.autoreverses=YES;
     animation.repeatCount=0;
     animation.removedOnCompletion=NO;
     animation.fillMode=kCAFillModeForwards;
     [view.layer addAnimation:animation forKey:@"zoom"];
     */
    
    /**
     * 非弹性放大动画
     */
    func biggerAnimation(biggerMultiple: CGFloat) {
        
        let animation = CABasicAnimation(keyPath: "transform.scale")
        
        animation.fromValue = 1
        animation.toValue = biggerMultiple
        animation.duration = 0.3
        
        // 动画完成后是否回归
        //        animation.autoreverses = true
        
        animation.repeatCount = 1
        animation.isRemovedOnCompletion = false
        animation.fillMode = .forwards
        
        self.layer.add(animation, forKey: "bounce_bigger")
        
    }
    
    /**
     * 弹性放大动画
     */
    func biggerAnimationWithBounce(biggerMultiple: CGFloat) {
        
        
        let animation = CAKeyframeAnimation(keyPath: "transform.scale")
        
        animation.values = [1, biggerMultiple * 1.1, biggerMultiple * 0.9, biggerMultiple]
        animation.duration = 0.5
        
        var timingFunctions = [CAMediaTimingFunction]()
        
        for _ in animation.values! {
            
            let functionName = CAMediaTimingFunction(name: .linear)
            timingFunctions.append(functionName)
        }
        
        animation.timingFunctions = timingFunctions
        
        animation.isRemovedOnCompletion = false
        animation.fillMode = .forwards
        self.layer.add(animation, forKey: "bounce_bigger")
    }
    
    /**
     * 回归弹性动画
     */
    func goIdentity(currentMultiple: CGFloat) {
        
        let animation = CABasicAnimation(keyPath: "transform.scale")
        
        animation.fromValue = currentMultiple
        animation.toValue = 1
        animation.duration = 0.25
        
        // 动画完成后是否回归
        //        animation.autoreverses = true
        
        animation.repeatCount = 1
        animation.isRemovedOnCompletion = false
        animation.fillMode = .forwards
        
        self.layer.add(animation, forKey: "goIdentity")
    }
    
    /**
     * 自旋转动画
     */
    func rotationAnimation(duration: CFTimeInterval = 1, repeatCount: Float = MAXFLOAT, key: String = "transform.rotation.z") {
        
        // 1. 创建动画
        let rotationAnim = CABasicAnimation(keyPath: key)
        
        // 2. 设置动画属性
        rotationAnim.fromValue = 0 // 开始角度
        rotationAnim.toValue = Double.pi * 2 //结束角度
        rotationAnim.repeatCount = repeatCount // 重复次数
        rotationAnim.duration = duration // 一圈所需要的时间
        rotationAnim.isRemovedOnCompletion = false //默认是true，切换到其他控制器再回来，动画效果会消失，需要设置成false，动画就不会停了
        rotationAnim.fillMode = .forwards
        
        self.layer.add(rotationAnim, forKey: key) // 给需要旋转的view增加动画
    }
}



public extension CALayer {
    
    
    enum AnimationStatus {
        case origin                 // 初始状态
        case inAnimating            // 动画执行中...
        case pause                  // 动画暂停状态
    }
    
    /** 记录动画状态 */
    var animationStatus: AnimationStatus? {
        get {
            if let a = objc_getAssociatedObject(self, &animationStatusKey) as? AnimationStatus {
                return a
            }
            return .origin
        }
        set {
            
            // zwarning: OBJC_ASSOCIATION_ASSIGN 使用这个坑逼, 不能正确存储
            objc_setAssociatedObject(self, &animationStatusKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    
    /**
     * 暂停动画
     */
    func pauseAnimation() {
        
        let pausedTime = self.convertTime(CACurrentMediaTime(), from: nil)
        self.speed = 0
        self.timeOffset = pausedTime
    }
    
    /**
     * 继续动画
     */
    func resumeAnimation() {
        
        let pausedTime = self.timeOffset
        self.speed = 1
        self.timeOffset = 0
        self.beginTime = 0
        
        let timeSincePause = self.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
        self.beginTime = timeSincePause
    }

    /**
     * 能监听结束回调的 自旋转动画
     */
    func rotationAnimation(duration: CFTimeInterval = 1, repeatCount: Float = MAXFLOAT, key: String = "transform.rotation.z", startAction: (()->Void)? = nil, stopAction: (()->Void)? = nil) {
                
        // 1. 创建动画
        let rotationAnim = CABasicAnimation(keyPath: key)
        
        // 2. 设置动画属性
        rotationAnim.fromValue = 0 // 开始角度
        rotationAnim.toValue = Double.pi * 2 //结束角度
        rotationAnim.repeatCount = repeatCount // 重复次数
        rotationAnim.duration = duration // 一圈所需要的时间
        rotationAnim.isRemovedOnCompletion = false //默认是true，切换到其他控制器再回来，动画效果会消失，需要设置成false，动画就不会停了
        rotationAnim.fillMode = .forwards
        
        addAnimation(rotationAnim, forKey: key, startAction: startAction, stopAction: stopAction)
    }
    
    
    func addAnimation(_ anim: CAAnimation, forKey key: String? = nil, startAction: (()->Void)? = nil, stopAction: (()->Void)? = nil) {
        
        // 如果传入了开始或者结束回调的action, 并且代理为空, 就赋值代理
        if stopAction != nil {
            if anim.delegate == nil {
                anim.delegate = AnimationDelegate()
            }
            
            guard let aniDelegate = anim.delegate as? AnimationDelegate else {return}
            
            let selfStartAction = { [weak self] in
                
                self?.animationStatus = .inAnimating
                startAction?()
            }
            
            let selfStopAction = { [weak self] in
                
                self?.animationStatus = .origin
                stopAction?()
            }
            
            aniDelegate.startAction = selfStartAction
            aniDelegate.stopAction = selfStopAction
        }
        
        self.add(anim, forKey: key)
    }
}


/**
 * 给shapeLayer添加绘图动画
 */
public extension CAShapeLayer {
    
    /**
     * addAnimation如果放在layoutSubviews里面调, 会有问题
     * 因为layout会调用过多次, 假设调用两次, 第一次添加的animation, 虽然进行了remove处理, 但是还是会触发didStop方法, 导致stop回调调用多次
     */
    func addStrokeAnimation(duration: TimeInterval, stopAction: (()->())? = nil) {
        
        if self.animationStatus == .origin {
            let animationKey = "addStrokeAnimation"
            if let _ = self.animation(forKey: animationKey) {
                self.removeAnimation(forKey: animationKey)
            }
            
            // 添加动画 strokeEnd正常绘制效果，strokeStart逐渐消失效果
            let animation = CABasicAnimation(keyPath: "strokeEnd")
            
            animation.fromValue = 0 //初始值
            animation.toValue = 1 //结束值
            animation.repeatCount = 1 //重复次数
            animation.duration = duration //动画时间
            animation.beginTime = CACurrentMediaTime()
            addAnimation(animation, forKey: animationKey, startAction: nil, stopAction: stopAction)
        }

    }
}



/** 核心动画代理对象 */
open class AnimationDelegate: NSObject, CAAnimationDelegate {
    
    var startAction: (()->Void)?
    
    var stopAction: (()->Void)?

    public func animationDidStart(_ anim: CAAnimation) {
        
        startAction?()
    }
    
    public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        
        stopAction?()
    }
}

